package cn.web;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.FlushMode;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate5.SessionFactoryUtils;
import org.springframework.orm.hibernate5.SessionHolder;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.support.TransactionSynchronizationManager;

@SuppressWarnings("unchecked")
@Repository
public class BaseDao {
    @Autowired
    protected SessionFactory sessionFactory;

    protected Session getCurrentSession() {
        return this.sessionFactory.getCurrentSession();
    }

    public <T> T getById(Class<T> clazz, Serializable id) {
        return getCurrentSession().get(clazz, id);
    }

    public void batchSave(Collection<? extends Object> entitys) {
        for (Object object : entitys) {
            this.sessionFactory.getCurrentSession().save(object);
        }
    }

    public <T> List<T> getByIds(Class<T> clazz, Serializable[] ids) {
        return getCurrentSession().createQuery("FROM " + clazz.getSimpleName() + " WHERE id IN (:ids)")
                .setParameter("ids", ids).list();
    }

    public <T> List<T> getAll(Class<T> clazz) {
        return getCurrentSession().createQuery("FROM " + clazz.getSimpleName()).list();
    }

    public <T> void deleteById(Class<T> clazz, Serializable id) {
        getCurrentSession().createQuery("DELETE FROM " + clazz.getSimpleName() + " WHERE id = ?").setParameter(0, id)
                .executeUpdate();
    }

    public <T> void deleteByIds(Class<T> clazz, Serializable[] ids) {
        for (Serializable id : ids) {
            deleteById(clazz, id);
        }
    }

    public <T> Serializable save(T entity) {
        return getCurrentSession().save(entity);
    }

    public <T> long getTotal(Class<T> clazz) {
        return (long) getCurrentSession().createQuery("SELECT COUNT(*) FROM " + clazz.getSimpleName()).uniqueResult();
    }

    public <T> List<T> getByPage(Class<T> clazz, int offset, int limit) {
        Criteria criteria = getCurrentSession().createCriteria(clazz);
        criteria.setFirstResult(offset);
        criteria.setMaxResults(limit);
        return criteria.list();
    }

    public <T> List<T> getByPage(Class<T> clazz, int offset, int limit, Criterion... expressions) {
        Criteria criteria = getCurrentSession().createCriteria(clazz);
        for (Criterion expression : expressions) {
            criteria.add(expression);
        }
        criteria.setFirstResult(offset);
        criteria.setMaxResults(limit);
        return criteria.list();
    }

    public <T> T getFirst(Class<T> t, Order order, Criterion... expressions) {
        Criteria criteria = this.sessionFactory.getCurrentSession().createCriteria(t);
        for (Criterion expression : expressions) {
            criteria.add(expression);
        }
        criteria.setFirstResult(0);
        criteria.setMaxResults(1);
        if (order != null)
            criteria.addOrder(order);

        List<T> ts = criteria.list();
        if (ts.size() > 0)
            return ts.get(0);
        else
            return null;
    }

    public <T> T getFirst(Class<T> t, Criterion... expressions) {
        return this.getFirst(t, null, expressions);
    }

    public <T> List<T> queryForList(Class<T> t, Order order, Criterion... criterions) {
        Criteria criteria = this.sessionFactory.getCurrentSession().createCriteria(t);
        for (Criterion criterion : criterions) {
            criteria.add(criterion);
        }
        if (order != null)
            criteria.addOrder(order);
        return criteria.list();
    }

    public <T> List<T> queryForList(String hql, Object... objects) {
        Query query = this.sessionFactory.getCurrentSession().createQuery(hql);
        int i = 0;
        for (Object object : objects) {
            query.setParameter(i++, object);
        }
        return query.list();
    }

    public <T> List<T> queryForList(Class<T> t, Criterion... criterions) {
        return this.queryForList(t, null, criterions);
    }

    public void delete(Object objet) {
        Session session = this.sessionFactory.getCurrentSession();
        session.delete(objet);
        session.flush();
    }

    public void update(Object objet) {
        Session session = this.sessionFactory.getCurrentSession();
        session.update(objet);
        session.flush();
    }

    public <T> Long getTotal(Class<T> t, Criterion... criterions) {
        Criteria criteria = this.sessionFactory.getCurrentSession().createCriteria(t);
        for (Criterion criterion : criterions) {
            criteria.add(criterion);
        }
        criteria.setProjection(Projections.rowCount());
        return (Long) criteria.uniqueResult();
    }

    public <T> List<T> queryForList(Class<T> t, int start, int max, Order order, Criterion... criterions) {
        Criteria criteria = this.sessionFactory.getCurrentSession().createCriteria(t);
        for (Criterion criterion : criterions) {
            criteria.add(criterion);
        }
        if (order != null)
            criteria.addOrder(order);
        criteria.setFirstResult(start);
        criteria.setMaxResults(max);
        return criteria.list();
    }

    public void execHql(String hql, Object... objects) {
        Session session = this.sessionFactory.getCurrentSession();
        Query query = session.createQuery(hql);
        for (int i = 0; i < objects.length; i++) {
            query.setParameter(i, objects[i]);
        }
        query.executeUpdate();
        session.flush();
        session.clear();
    }

    public void saveOrUpdate(Object object) {
        Session session = this.sessionFactory.getCurrentSession();
        session.saveOrUpdate(object);
        session.flush();
    }

    public <T> T queryForUnique(Class<T> t, Criterion... expressions) {
        Criteria query = this.sessionFactory.getCurrentSession().createCriteria(t);
        for (Criterion expression : expressions) {
            query.add(expression);
        }
        return (T) query.uniqueResult();
    }

    public <T> T queryForUnique(String hql, Object... objects) {
        Query query = this.sessionFactory.getCurrentSession().createQuery(hql);
        int i = 0;
        for (Object object : objects) {
            query.setParameter(i++, object);
        }
        return (T) query.uniqueResult();
    }

    // 将session绑定到线程
//    public boolean bindHibernateSessionToThread() {
//        if (TransactionSynchronizationManager.hasResource(this.sessionFactory)) {
//            return true;
//        } else {
//            Session session = this.sessionFactory.openSession();
//            session.setFlushMode(FlushMode.MANUAL);
//            SessionHolder sessionHolder = new SessionHolder(session);
//            TransactionSynchronizationManager.bindResource(this.sessionFactory, sessionHolder);
//        }
//        return false;
//    }
//
//    public void closeHibernateSessionFromThread(boolean participate) {
//        if (!participate) {
//            SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.unbindResource(sessionFactory);
//            SessionFactoryUtils.closeSession(sessionHolder.getSession());
//        }
//    }
}
